import json
import time
from logging import warning

import serial
from links.abstract_link import AbstractLink


class SerialPortLinkDataKeys:
    PORT_NAME = "port_name"
    BAUD_RATE = "baud_rate"
    DATA_BITS = "data_bits"
    STOP_BITS = "stop_bits"
    PARITY = "parity"
    FLOW_CONTROL = "flow_control"


def print_serial_port_io_data_keys(ctx: dict):
    for key in ctx.keys():
        print(key)


class SerialPortLink(AbstractLink):
    def __init__(self):
        super().__init__()

    def run(self):
        parameters = self.save()
        parameters_str = json.dumps(parameters, separators=(",", ":"))
        print("serial port io parameters is: " + parameters_str)

        keys = SerialPortLinkDataKeys()
        port_name = parameters.get(keys.PORT_NAME)
        baud_rate = parameters.get(keys.BAUD_RATE)
        data_bits = parameters.get(keys.DATA_BITS)
        stop_bits = parameters.get(keys.STOP_BITS)
        parity = parameters.get(keys.PARITY)
        flow_control = parameters.get(keys.FLOW_CONTROL)

        serial_port = serial.Serial(
            port=port_name,
            baudrate=int(baud_rate),
            bytesize=serial.EIGHTBITS,
            stopbits=serial.STOPBITS_ONE,
            parity=serial.PARITY_NONE,
            xonxoff=flow_control == "XON/XOFF(S)",
            rtscts=flow_control == "RTS/CTS(H)",
        )

        # try:
        #     serial_port.open()
        # except serial.SerialException as e:
        #     serial_port.close()
        #     print("Error opening serial port: " + str(e))

        if not serial_port.is_open:
            warning("Serial port is not open")
            serial_port.close()
            return

        while True:
            if self.invoke_terminal():
                print("Serial port io terminal invoked")
                serial_port.close()
                break
            if serial_port.in_waiting > 0:
                data = serial_port.read(serial_port.in_waiting)
                item = {"data": data, "flag": port_name, "rx": True}
                self.output_queue.put(item)
            if not self.input_queue.empty():
                data = self.input_queue.get()
                ret = serial_port.write(data)
                if ret != len(data):
                    warning("Serial port write error")
                    return
                item = {"data": data, "flag": port_name, "rx": False}
                self.output_queue.put(item)

            time.sleep(0.01)
